if (Ext.version == '3.0') {
	Ext.override(Ext.grid.GridView, {
		ensureVisible : function(row, col, hscroll) {

			var resolved = this.resolveCell(row, col, hscroll);
			if (!resolved || !resolved.row) {
				return;
			}

			var rowEl = resolved.row, cellEl = resolved.cell, c = this.scroller.dom, ctop = 0, p = rowEl, stop = this.el.dom;

			var p = rowEl, stop = this.el.dom;
			while (p && p != stop) {
				ctop += p.offsetTop;
				p = p.offsetParent;
			}
			ctop -= this.mainHd.dom.offsetHeight;

			var cbot = ctop + rowEl.offsetHeight;

			var ch = c.clientHeight;
			var stop = parseInt(c.scrollTop, 10);
			var sbot = stop + ch;

			if (ctop < stop) {
				c.scrollTop = ctop;
			} else if (cbot > sbot) {
				c.scrollTop = cbot - ch;
			}

			if (hscroll !== false) {
				var cleft = parseInt(cellEl.offsetLeft, 10);
				var cright = cleft + cellEl.offsetWidth;

				var sleft = parseInt(c.scrollLeft, 10);
				var sright = sleft + c.clientWidth;
				if (cleft < sleft) {
					c.scrollLeft = cleft;
				} else if (cright > sright) {
					c.scrollLeft = cright - c.clientWidth;
				}
			}
			return this.getResolvedXY(resolved);
		}
	});
}

Ext.namespace('Ext.ux.maximgb.tg');

/**
 * This class shouldn't be created directly use NestedSetStore or
 * AdjacencyListStore instead.
 * 
 * @abstract
 */
Ext.ux.maximgb.tg.AbstractTreeStore = Ext.extend(Ext.data.Store, {
	/**
	 * @cfg {String} is_leaf_field_name Record leaf flag field name.
	 */
	leaf_field_name : 'Leaf',

	/**
	 * Current page offset.
	 * 
	 * @access private
	 */
	page_offset : 0,

	/**
	 * Current active node.
	 * 
	 * @access private
	 */
	active_node : null,

	/**
	 * @constructor
	 */
	constructor : function(config) {
		Ext.ux.maximgb.tg.AbstractTreeStore.superclass.constructor.call(this,
				config);

		if (!this.paramNames.active_node) {
			this.paramNames.active_node = 'anode';
		}

		this.addEvents(
				/**
				 * @event beforeexpandnode Fires before node expand. Return
				 *        false to cancel operation. param {AbstractTreeStore}
				 *        this param {Record} record
				 */
				'beforeexpandnode',
				/**
				 * @event expandnode Fires after node expand. param
				 *        {AbstractTreeStore} this param {Record} record
				 */
				'expandnode',
				/**
				 * @event expandnodefailed Fires when expand node operation is
				 *        failed. param {AbstractTreeStore} this param {id}
				 *        Record id param {Record} Record, may be undefined
				 */
				'expandnodefailed',
				/**
				 * @event beforecollapsenode Fires before node collapse. Return
				 *        false to cancel operation. param {AbstractTreeStore}
				 *        this param {Record} record
				 */
				'beforecollapsenode',
				/**
				 * @event collapsenode Fires after node collapse. param
				 *        {AbstractTreeStore} this param {Record} record
				 */
				'collapsenode',
				/**
				 * @event beforeactivenodechange Fires before active node
				 *        change. Return false to cancel operation. param
				 *        {AbstractTreeStore} this param {Record} old active
				 *        node record param {Record} new active node record
				 */
				'beforeactivenodechange',
				/**
				 * @event activenodechange Fires after active node change. param
				 *        {AbstractTreeStore} this param {Record} old active
				 *        node record param {Record} new active node record
				 */
				'activenodechange');
	},

	// Store methods.
	// -----------------------------------------------------------------------------------------------
	/**
	 * Removes record and all its descendants.
	 * 
	 * @access public
	 * @param {Record}
	 *            record Record to remove.
	 */
	remove : function(record) {
		// ----- Modification start
		if (record === this.active_node) {
			this.setActiveNode(null);
		}
		this.removeNodeDescendants(record);
		// ----- End of modification
		Ext.ux.maximgb.tg.AbstractTreeStore.superclass.remove
				.call(this, record);
	},

	/**
	 * Removes node descendants.
	 * 
	 * @access private
	 */
	removeNodeDescendants : function(rc) {
		var i, len, children = this.getNodeChildren(rc);
		for (i = 0, len = children.length; i < len; i++) {
			this.remove(children[i]);
		}
	},

	/**
	 * Loads current active record data.
	 */
	load : function(options) {
		if (options) {
			if (options.params) {
				if (options.params[this.paramNames.active_node] === undefined) {
					options.params[this.paramNames.active_node] = this.active_node
							? this.active_node.id
							: null;
				}
			} else {
				options.params = {};
				options.params[this.paramNames.active_node] = this.active_node
						? this.active_node.id
						: null;
			}
		} else {
			options = {
				params : {}
			};
			options.params[this.paramNames.active_node] = this.active_node
					? this.active_node.id
					: null;
		}

		if (options.params[this.paramNames.active_node] !== null) {
			options.add = true;
		}

		return Ext.ux.maximgb.tg.AbstractTreeStore.superclass.load.call(this,
				options);
	},

	/**
	 * Called as a callback by the Reader during load operation.
	 * 
	 * @access private
	 */
	loadRecords : function(o, options, success) {

		if (!o || success === false) {
			if (success !== false) {
				this.fireEvent("load", this, [], options);
			}
			if (options.callback) {
				options.callback
						.call(options.scope || this, [], options, false);
			}
			return;
		}
		var r = o.records, t = o.totalRecords || r.length, page_offset = this
				.getPageOffsetFromOptions(options), loaded_node_id = this
				.getLoadedNodeIdFromOptions(options), loaded_node, i, len, record, idx, updated, self = this;
		if (!options || options.add !== true/* || loaded_node_id === null */) {
			if (this.pruneModifiedRecords) {
				this.modified = [];
			}
			for (var i = 0, len = r.length; i < len; i++) {
				r[i].join(this);
			}
			if (this.snapshot) {
				this.data = this.snapshot;
				delete this.snapshot;
			}
			this.data.clear();
			this.data.addAll(r);
			this.page_offset = page_offset;
			this.totalLength = t;
			this.applySort();
			this.fireEvent("datachanged", this);
		} else {
			if (loaded_node_id) {
				loaded_node = this.getById(loaded_node_id);
			}
			if (loaded_node) {
				this.setNodeChildrenOffset(loaded_node, page_offset);
				this.setNodeChildrenTotalCount(loaded_node, Math.max(t,
								r.length));
				this.removeNodeDescendants(loaded_node);
			}
			this.suspendEvents();
			updated = {};
			for (i = 0, len = r.length; i < len; i++) {
				record = r[i];
				idx = this.indexOfId(record.id);
				if (idx == -1) {
					updated[record.id] = false;
				} else {
					updated[record.id] = true;
					this.setNodeExpanded(record, this.isExpandedNode(this
									.getAt(idx)));
				}
				this.add(record);
			}
			this.applySort();
			this.resumeEvents();

			r.sort(function(r1, r2) {
				var idx1 = self.data.indexOf(r1), idx2 = self.data.indexOf(r2), r;

				if (idx1 > idx2) {
					r = 1;
				} else {
					r = -1;
				}
				return r;
			});

			for (i = 0, len = r.length; i < len; i++) {
				record = r[i];
				if (updated[record.id] == true) {
					this.fireEvent('update', this, record,
							Ext.data.Record.COMMIT);
				} else {
					this.fireEvent("add", this, [record], this.data
									.indexOf(record));
				}
			}
		}
		this.fireEvent("load", this, r, options);
		if (options.callback) {
			options.callback.call(options.scope || this, r, options, true);
		}
	},

	/**
	 * Sort the Records.
	 * 
	 * @access public
	 */
	sort : function(fieldName, dir) {
		if (this.remoteSort) {
			this.setActiveNode(null);
			if (this.lastOptions) {
				this.lastOptions.add = false;
				if (this.lastOptions.params) {
					this.lastOptions.params[this.paramNames.active_node] = null;
				}
			}
		}

		return Ext.ux.maximgb.tg.AbstractTreeStore.superclass.sort.call(this,
				fieldName, dir);
	},

	/**
	 * Applyes current sort method.
	 * 
	 * @access private
	 */
	applySort : function() {
		if (this.sortInfo && !this.remoteSort) {
			var s = this.sortInfo, f = s.field;
			this.sortData(f, s.direction);
		}
		// ----- Modification start
		else {
			this.applyTreeSort();
		}
		// ----- End of modification
	},

	/**
	 * Sorts data according to sort params and then applyes tree sorting.
	 * 
	 * @access private
	 */
	sortData : function(f, direction) {
		direction = direction || 'ASC';
		var st = this.fields.get(f).sortType;
		var fn = function(r1, r2) {
			var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
			return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
		};
		this.data.sort(direction, fn);
		if (this.snapshot && this.snapshot != this.data) {
			this.snapshot.sort(direction, fn);
		}
		// ----- Modification start
		this.applyTreeSort();
		// ----- End of modification
	},

	// Tree support methods.
	// -----------------------------------------------------------------------------------------------

	/**
	 * Sorts store data with respect to nodes parent-child relation. Every child
	 * node will be positioned after its parent.
	 * 
	 * @access public
	 */
	applyTreeSort : function() {
		var i, len, temp, rec, records = [], roots = this.getRootNodes();

		// Sorting data
		for (i = 0, len = roots.length; i < len; i++) {
			rec = roots[i];
			records.push(rec);
			this.collectNodeChildrenTreeSorted(records, rec);
		}

		if (records.length > 0) {
			this.data.clear();
			this.data.addAll(records);
		}

		// Sorting the snapshot if one present.
		if (this.snapshot && this.snapshot !== this.data) {
			temp = this.data;
			this.data = this.snapshot;
			this.snapshot = null;
			this.applyTreeSort();
			this.snapshot = this.data;
			this.data = temp;
		}
	},

	/**
	 * Recusively collects rec descendants and adds them to records[] array.
	 * 
	 * @access private
	 * @param {Record[]}
	 *            records
	 * @param {Record}
	 *            rec
	 */
	collectNodeChildrenTreeSorted : function(records, rec) {
		var i, len, child, children = this.getNodeChildren(rec);

		for (i = 0, len = children.length; i < len; i++) {
			child = children[i];
			records.push(child);
			this.collectNodeChildrenTreeSorted(records, child);
		}
	},

	/**
	 * Returns current active node.
	 * 
	 * @access public
	 * @return {Record}
	 */
	getActiveNode : function() {
		return this.active_node;
	},

	/**
	 * Sets active node.
	 * 
	 * @access public
	 * @param {Record}
	 *            rc Record to set active.
	 */
	setActiveNode : function(rc) {
		if (this.active_node !== rc) {
			if (rc) {
				if (this.data.indexOf(rc) != -1) {
					if (this.fireEvent('beforeactivenodechange', this,
							this.active_node, rc) !== false) {
						this.active_node = rc;
						this.fireEvent('activenodechange', this,
								this.active_node, rc);
					}
				} else {
					throw "Given record is not from the store.";
				}
			} else {
				if (this.fireEvent('beforeactivenodechange', this,
						this.active_node, rc) !== false) {
					this.active_node = rc;
					this.fireEvent('activenodechange', this, this.active_node,
							rc);
				}
			}
		}
	},

	/**
	 * Returns true if node is expanded.
	 * 
	 * @access public
	 * @param {Record}
	 *            rc
	 */
	isExpandedNode : function(rc) {
		return rc.ux_maximgb_tg_expanded === true;
	},

	/**
	 * Sets node expanded flag.
	 * 
	 * @access private
	 */
	setNodeExpanded : function(rc, value) {
		rc.ux_maximgb_tg_expanded = value;
	},

	/**
	 * Returns true if node's ancestors are all expanded - node is visible.
	 * 
	 * @access public
	 * @param {Record}
	 *            rc
	 */
	isVisibleNode : function(rc) {
		var i, len, ancestors = this.getNodeAncestors(rc), result = true;

		for (i = 0, len = ancestors.length; i < len; i++) {
			result = result && this.isExpandedNode(ancestors[i]);
			if (!result) {
				break;
			}
		}

		return result;
	},

	/**
	 * Returns true if node is a leaf.
	 * 
	 * @access public
	 * @return {Boolean}
	 */
	isLeafNode : function(rc) {
		return rc.get(this.leaf_field_name) == true;
	},

	/**
	 * Returns true if node was loaded.
	 * 
	 * @access public
	 * @return {Boolean}
	 */
	isLoadedNode : function(rc) {
		var result;

		if (rc.ux_maximgb_tg_loaded !== undefined) {
			result = rc.ux_maximgb_tg_loaded;
		} else if (this.isLeafNode(rc) || this.hasChildNodes(rc)) {
			result = true;
		} else {
			result = false;
		}

		return result;
	},

	/**
	 * Sets node loaded state.
	 * 
	 * @access private
	 * @param {Record}
	 *            rc
	 * @param {Boolean}
	 *            value
	 */
	setNodeLoaded : function(rc, value) {
		rc.ux_maximgb_tg_loaded = value;
	},

	/**
	 * Returns node's children offset.
	 * 
	 * @access public
	 * @param {Record}
	 *            rc
	 * @return {Integer}
	 */
	getNodeChildrenOffset : function(rc) {
		return rc.ux_maximgb_tg_offset || 0;
	},

	/**
	 * Sets node's children offset.
	 * 
	 * @access private
	 * @param {Record}
	 *            rc
	 * @parma {Integer} value
	 */
	setNodeChildrenOffset : function(rc, value) {
		rc.ux_maximgb_tg_offset = value;
	},

	/**
	 * Returns node's children total count
	 * 
	 * @access public
	 * @param {Record}
	 *            rc
	 * @return {Integer}
	 */
	getNodeChildrenTotalCount : function(rc) {
		return rc.ux_maximgb_tg_total || 0;
	},

	/**
	 * Sets node's children total count.
	 * 
	 * @access private
	 * @param {Record}
	 *            rc
	 * @param {Integer}
	 *            value
	 */
	setNodeChildrenTotalCount : function(rc, value) {
		rc.ux_maximgb_tg_total = value;
	},

	/**
	 * Collapses node.
	 * 
	 * @access public
	 * @param {Record}
	 *            rc
	 * @param {Record}
	 *            rc Node to collapse.
	 */
	collapseNode : function(rc) {
		if (this.isExpandedNode(rc)
				&& this.fireEvent('beforecollapsenode', this, rc) !== false) {
			this.setNodeExpanded(rc, false);
			this.fireEvent('collapsenode', this, rc);
		}
	},

	/**
	 * Expands node.
	 * 
	 * @access public
	 * @param {Record}
	 *            rc
	 */
	expandNode : function(rc) {
		var params;

		if (!this.isExpandedNode(rc)
				&& this.fireEvent('beforeexpandnode', this, rc) !== false) {
			// If node is already loaded then expanding now.
			if (this.isLoadedNode(rc)) {
				this.setNodeExpanded(rc, true);
				this.fireEvent('expandnode', this, rc);
			}
			// If node isn't loaded yet then expanding after load.
			else {
				params = {};
				params[this.paramNames.active_node] = rc.id;
				this.load({
							add : true,
							params : params,
							callback : this.expandNodeCallback,
							scope : this
						});
			}
		}
	},

	/**
	 * @access private
	 */
	expandNodeCallback : function(r, options, success) {
		var rc = this.getById(options.params[this.paramNames.active_node]);

		if (success && rc) {
			this.setNodeLoaded(rc, true);
			this.setNodeExpanded(rc, true);
			this.fireEvent('expandnode', this, rc);
		} else {
			this.fireEvent('expandnodefailed', this,
					options.params[this.paramNames.active_node], rc);
		}
	},

	/**
	 * Expands all nodes.
	 * 
	 * @access public
	 */
	expandAll : function() {
		var r, i, len, records = this.data.getRange();
		this.suspendEvents();
		for (i = 0, len = records.length; i < len; i++) {
			r = records[i];
			if (!this.isExpandedNode(r)) {
				this.expandNode(r);
			}
		}
		this.resumeEvents();
		this.fireEvent('datachanged', this);
	},

	/**
	 * Collapses all nodes.
	 * 
	 * @access public
	 */
	collapseAll : function() {
		var r, i, len, records = this.data.getRange();

		this.suspendEvents();
		for (i = 0, len = records.length; i < len; i++) {
			r = records[i];
			if (this.isExpandedNode(r)) {
				this.collapseNode(r);
			}
		}
		this.resumeEvents();
		this.fireEvent('datachanged', this);
	},

	/**
	 * Returns loaded node id from the load options.
	 * 
	 * @access public
	 */
	getLoadedNodeIdFromOptions : function(options) {
		var result = null;
		if (options && options.params
				&& options.params[this.paramNames.active_node]) {
			result = options.params[this.paramNames.active_node];
		}
		return result;
	},

	/**
	 * Returns start offset from the load options.
	 */
	getPageOffsetFromOptions : function(options) {
		var result = 0;
		if (options && options.params && options.params[this.paramNames.start]) {
			result = parseInt(options.params[this.paramNames.start], 10);
			if (isNaN(result)) {
				result = 0;
			}
		}
		return result;
	},

	// Public
	hasNextSiblingNode : function(rc) {
		return this.getNodeNextSibling(rc) !== null;
	},

	// Public
	hasPrevSiblingNode : function(rc) {
		return this.getNodePrevSibling(rc) !== null;
	},

	// Public
	hasChildNodes : function(rc) {
		return this.getNodeChildrenCount(rc) > 0;
	},

	// Public
	getNodeAncestors : function(rc) {
		var ancestors = [], parent;

		parent = this.getNodeParent(rc);
		while (parent) {
			ancestors.push(parent);
			parent = this.getNodeParent(parent);
		}

		return ancestors;
	},

	// Public
	getNodeChildrenCount : function(rc) {
		return this.getNodeChildren(rc).length;
	},

	// Public
	getNodeNextSibling : function(rc) {
		var siblings, parent, index, result = null;

		parent = this.getNodeParent(rc);
		if (parent) {
			siblings = this.getNodeChildren(parent);
		} else {
			siblings = this.getRootNodes();
		}

		index = siblings.indexOf(rc);

		if (index < siblings.length - 1) {
			result = siblings[index + 1];
		}

		return result;
	},

	// Public
	getNodePrevSibling : function(rc) {
		var siblings, parent, index, result = null;

		parent = this.getNodeParent(rc);
		if (parent) {
			siblings = this.getNodeChildren(parent);
		} else {
			siblings = this.getRootNodes();
		}

		index = siblings.indexOf(rc);
		if (index > 0) {
			result = siblings[index - 1];
		}

		return result;
	},

	// Abstract tree support methods.
	// -----------------------------------------------------------------------------------------------

	// Public - Abstract
	getRootNodes : function() {
		throw 'Abstract method call';
	},

	// Public - Abstract
	getNodeDepth : function(rc) {
		throw 'Abstract method call';
	},

	// Public - Abstract
	getNodeParent : function(rc) {
		throw 'Abstract method call';
	},

	// Public - Abstract
	getNodeChildren : function(rc) {
		throw 'Abstract method call';
	},

	// Public - Abstract
	addToNode : function(parent, child) {
		throw 'Abstract method call';
	},

	// Public - Abstract
	removeFromNode : function(parent, child) {
		throw 'Abstract method call';
	},

	// Paging support methods.
	// -----------------------------------------------------------------------------------------------
	/**
	 * Returns top level node page offset.
	 * 
	 * @access public
	 * @return {Integer}
	 */
	getPageOffset : function() {
		return this.page_offset;
	},

	/**
	 * Returns active node page offset.
	 * 
	 * @access public
	 * @return {Integer}
	 */
	getActiveNodePageOffset : function() {
		var result;

		if (this.active_node) {
			result = this.getNodeChildrenOffset(this.active_node);
		} else {
			result = this.getPageOffset();
		}

		return result;
	},

	/**
	 * Returns active node children count.
	 * 
	 * @access public
	 * @return {Integer}
	 */
	getActiveNodeCount : function() {
		var result;

		if (this.active_node) {
			result = this.getNodeChildrenCount(this.active_node);
		} else {
			result = this.getRootNodes().length;
		}

		return result;
	},

	/**
	 * Returns active node total children count.
	 * 
	 * @access public
	 * @return {Integer}
	 */
	getActiveNodeTotalCount : function() {
		var result;

		if (this.active_node) {
			result = this.getNodeChildrenTotalCount(this.active_node);
		} else {
			result = this.getTotalCount();
		}

		return result;
	}
});

/**
 * Tree store for adjacency list tree representation.
 */
Ext.ux.maximgb.tg.AdjacencyListStore = Ext.extend(
		Ext.ux.maximgb.tg.AbstractTreeStore, {
			/**
			 * @cfg {String} parent_id_field_name Record parent id field name.
			 */
			parent_id_field_name : 'ParentId',
			getRootNodes : function() {
				var i, len, result = [], records = this.data.getRange();

				for (i = 0, len = records.length; i < len; i++) {
					if (records[i].get(this.parent_id_field_name) == null) {
						result.push(records[i]);
					}
				}
				return result;
			},

			getNodeDepth : function(rc) {
				return this.getNodeAncestors(rc).length;
			},

			getNodeParent : function(rc) {
				return this.getById(rc.get(this.parent_id_field_name));
			},

			getNodeChildren : function(rc) {

				var i, len, result = [], records = this.data.getRange();
				for (i = 0, len = records.length; i < len; i++) {
					if (records[i].get(this.parent_id_field_name) == rc.id) {
						result.push(records[i]);
					}
				}

				return result;
			},

			addToNode : function(parent, child) {
				child.set(this.parent_id_field_name, parent.id);
				this.addSorted(child);
			},

			removeFromNode : function(parent, child) {
				this.remove(child);
			}
		});

Ext.reg('Ext.ux.maximgb.tg.AdjacencyListStore',
		Ext.ux.maximgb.tg.AdjacencyListStore);

/**
 * Tree store for nested set tree representation.
 */
Ext.ux.maximgb.tg.NestedSetStore = Ext.extend(
		Ext.ux.maximgb.tg.AbstractTreeStore, {
			/**
			 * @cfg {String} left_field_name Record NS-left bound field name.
			 */
			left_field_name : '_lft',

			/**
			 * @cfg {String} right_field_name Record NS-right bound field name.
			 */
			right_field_name : '_rgt',

			/**
			 * @cfg {String} level_field_name Record NS-level field name.
			 */
			level_field_name : 'Level',

			/**
			 * @cfg {Number} root_node_level Root node level.
			 */
			root_node_level : 1,

			getRootNodes : function() {
				var i, len, result = [], records = this.data.getRange();

				for (i = 0, len = records.length; i < len; i++) {
					if (records[i].get(this.level_field_name) == this.root_node_level) {
						result.push(records[i]);
					}
				}

				return result;
			},

			getNodeDepth : function(rc) {
				return rc.get(this.level_field_name) - this.root_node_level;
			},

			getNodeParent : function(rc) {
				var result = null, rec, records = this.data.getRange(), i, len, lft, r_lft, rgt, r_rgt, level, r_level;

				lft = rc.get(this.left_field_name);
				rgt = rc.get(this.right_field_name);
				level = rc.get(this.level_field_name);

				for (i = 0, len = records.length; i < len; i++) {
					rec = records[i];
					r_lft = rec.get(this.left_field_name);
					r_rgt = rec.get(this.right_field_name);
					r_level = rec.get(this.level_field_name);

					if (r_level == level - 1 && r_lft < lft && r_rgt > rgt) {
						result = rec;
						break;
					}
				}

				return result;
			},

			getNodeChildren : function(rc) {
				var lft, r_lft, rgt, r_rgt, level, r_level, records, rec, result = [];

				records = this.data.getRange();

				lft = rc.get(this.left_field_name);
				rgt = rc.get(this.right_field_name);
				level = rc.get(this.level_field_name);

				for (i = 0, len = records.length; i < len; i++) {
					rec = records[i];
					r_lft = rec.get(this.left_field_name);
					r_rgt = rec.get(this.right_field_name);
					r_level = rec.get(this.level_field_name);

					if (r_level == level + 1 && r_lft > lft && r_rgt < rgt) {
						result.push(rec);
					}
				}

				return result;
			}
		});

Ext.ux.maximgb.tg.GridView = Ext.extend(Ext.grid.GridView, {
	expanded_icon_class : 'ux-maximgb-tg-elbow-minus',
	last_expanded_icon_class : 'ux-maximgb-tg-elbow-end-minus',
	collapsed_icon_class : 'ux-maximgb-tg-elbow-plus',
	last_collapsed_icon_class : 'ux-maximgb-tg-elbow-end-plus',
	skip_width_update_class : 'ux-maximgb-tg-skip-width-update',

	// private - overriden
	initTemplates : function() {
		var ts = this.templates || {};

		if (!ts.row) {
			ts.row = new Ext.Template(
					'<div class="x-grid3-row ux-maximgb-tg-level-{level} {alt}" style="{tstyle} {display_style}">',
					'<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
					'<tbody>',
					'<tr>{cells}</tr>',
					(this.enableRowBody
							? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}">'
									+ '<td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on">'
									+ '<div class="x-grid3-row-body">{body}</div>'
									+ '</td>' + '</tr>'
							: ''), '</tbody>', '</table>', '</div>');
		}

		if (!ts.mastercell) {
			ts.mastercell = new Ext.Template(
					'<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
					'<div class="ux-maximgb-tg-mastercell-wrap">', // This is
					// for
					// editor to
					// place
					// itself
					// right
					'{treeui}',
					'<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
					'</div>', '</td>');
		}

		if (!ts.treeui) {
			ts.treeui = new Ext.Template(
					'<div class="ux-maximgb-tg-uiwrap" style="width: {wrap_width}px">',
					'{elbow_line}',
					'<div style="left: {left}px" class="{cls}">&#160;</div>',
					'</div>');
		}

		if (!ts.elbow_line) {
			ts.elbow_line = new Ext.Template('<div style="left: {left}px" class="{cls}">&#160;</div>');
		}

		this.templates = ts;
		Ext.ux.maximgb.tg.GridView.superclass.initTemplates.call(this);
	},

	// Private - Overriden
	doRender : function(cs, rs, ds, startRow, colCount, stripe) {
		var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount - 1;
		var tstyle = 'width:' + this.getTotalWidth() + ';';
		// buffers
		var buf = [], cb, c, p = {}, rp = {
			tstyle : tstyle
		}, r;
		for (var j = 0, len = rs.length; j < len; j++) {
			r = rs[j];
			cb = [];
			var rowIndex = (j + startRow);

			var row_render_res = this.renderRow(r, rowIndex, colCount, ds,
					this.cm.getTotalWidth());

			if (row_render_res === false) {
				for (var i = 0; i < colCount; i++) {
					c = cs[i];
					p.id = c.id;
					p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last
							? 'x-grid3-cell-last '
							: '');
					p.attr = p.cellAttr = "";
					p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
					p.style = c.style;
					if (Ext.isEmpty(p.value)) {
						p.value = "&#160;";
					}
					if (this.markDirty && r.dirty
							&& typeof r.modified[c.name] !== 'undefined') {
						p.css += ' x-grid3-dirty-cell';
					}
					// ----- Modification start
					if (c.id == this.grid.master_column_id) {
						p.treeui = this.renderCellTreeUI(r, ds);
						ct = ts.mastercell;
					} else {
						ct = ts.cell;
					}
					// ----- End of modification
					cb[cb.length] = ct.apply(p);
				}
			} else {
				cb.push(row_render_res);
			}

			var alt = [];
			if (stripe && ((rowIndex + 1) % 2 == 0)) {
				alt[0] = "x-grid3-row-alt";
			}
			if (r.dirty) {
				alt[1] = " x-grid3-dirty-row";
			}
			rp.cols = colCount;
			if (this.getRowClass) {
				alt[2] = this.getRowClass(r, rowIndex, rp, ds);
			}
			rp.alt = alt.join(" ");
			rp.cells = cb.join("");
			// ----- Modification start
			if (!ds.isVisibleNode(r)) {
				rp.display_style = 'display: none;';
			} else {
				rp.display_style = '';
			}
			rp.level = ds.getNodeDepth(r);
			// ----- End of modification
			buf[buf.length] = rt.apply(rp);
		}
		return buf.join("");
	},

	renderCellTreeUI : function(record, store) {
		var tpl = this.templates.treeui, line_tpl = this.templates.elbow_line, tpl_data = {}, rec, parent, depth = level = store
				.getNodeDepth(record);

		tpl_data.wrap_width = (depth + 1) * 16;
		if (level > 0) {
			tpl_data.elbow_line = '';
			rec = record;
			left = 0;
			while (level--) {
				parent = store.getNodeParent(rec);
				if (parent) {
					if (store.hasNextSiblingNode(parent)) {
						tpl_data.elbow_line = line_tpl.apply({
									left : level * 16,
									cls : 'ux-maximgb-tg-elbow-line'
								}) + tpl_data.elbow_line;
					} else {
						tpl_data.elbow_line = line_tpl.apply({
									left : level * 16,
									cls : 'ux-maximgb-tg-elbow-empty'
								}) + tpl_data.elbow_line;
					}
				} else {
					throw ["Tree inconsistency can't get level ", level + 1,
							" node(id=", rec.id, ") parent."].join("");
				}
				rec = parent;
			}
		}
		if (store.isLeafNode(record)) {
			if (store.hasNextSiblingNode(record)) {
				tpl_data.cls = 'ux-maximgb-tg-elbow';
			} else {
				tpl_data.cls = 'ux-maximgb-tg-elbow-end';
			}
		} else {
			tpl_data.cls = 'ux-maximgb-tg-elbow-active ';
			if (store.isExpandedNode(record)) {
				if (store.hasNextSiblingNode(record)) {
					tpl_data.cls += this.expanded_icon_class;
				} else {
					tpl_data.cls += this.last_expanded_icon_class;
				}
			} else {
				if (store.hasNextSiblingNode(record)) {
					tpl_data.cls += this.collapsed_icon_class;
				} else {
					tpl_data.cls += this.last_collapsed_icon_class;
				}
			}
		}
		tpl_data.left = 1 + depth * 16;

		return tpl.apply(tpl_data);
	},

	// Template method
	renderRow : function(record, index, col_count, ds, total_width) {
		return false;
	},

	// private - overriden
	afterRender : function() {
		Ext.ux.maximgb.tg.GridView.superclass.afterRender.call(this);
		this.updateAllColumnWidths();
	},

	// private - overriden to support missing column td's case, if row is
	// rendered by renderRow()
	// method.
	updateAllColumnWidths : function() {
		var tw = this.getTotalWidth(), clen = this.cm.getColumnCount(), ws = [], len, i;
		for (i = 0; i < clen; i++) {
			ws[i] = this.getColumnWidth(i);
		}
		this.innerHd.firstChild.style.width = this.getOffsetWidth();
		this.innerHd.firstChild.firstChild.style.width = tw;
		this.mainBody.dom.style.width = tw;
		for (i = 0; i < clen; i++) {
			var hd = this.getHeaderCell(i);
			hd.style.width = ws[i];
		}

		var ns = this.getRows(), row, trow;
		for (i = 0, len = ns.length; i < len; i++) {
			row = ns[i];
			row.style.width = tw;
			if (row.firstChild) {
				row.firstChild.style.width = tw;
				trow = row.firstChild.rows[0];
				for (var j = 0; j < clen && j < trow.childNodes.length; j++) {
					if (!Ext.fly(trow.childNodes[j])
							.hasClass(this.skip_width_update_class)) {
						trow.childNodes[j].style.width = ws[j];
					}
				}
			}
		}

		this.onAllColumnWidthsUpdated(ws, tw);
	},

	// private - overriden to support missing column td's case, if row is
	// rendered by renderRow()
	// method.
	updateColumnWidth : function(col, width) {
		var w = this.getColumnWidth(col);
		var tw = this.getTotalWidth();
		this.innerHd.firstChild.style.width = this.getOffsetWidth();
		this.innerHd.firstChild.firstChild.style.width = tw;
		this.mainBody.dom.style.width = tw;
		var hd = this.getHeaderCell(col);
		hd.style.width = w;

		var ns = this.getRows(), row;
		for (var i = 0, len = ns.length; i < len; i++) {
			row = ns[i];
			row.style.width = tw;
			if (row.firstChild) {
				row.firstChild.style.width = tw;
				if (col < row.firstChild.rows[0].childNodes.length) {
					if (!Ext.fly(row.firstChild.rows[0].childNodes[col])
							.hasClass(this.skip_width_update_class)) {
						row.firstChild.rows[0].childNodes[col].style.width = w;
					}
				}
			}
		}

		this.onColumnWidthUpdated(col, w, tw);
	},

	// private - overriden to support missing column td's case, if row is
	// rendered by renderRow()
	// method.
	updateColumnHidden : function(col, hidden) {
		var tw = this.getTotalWidth();
		this.innerHd.firstChild.style.width = this.getOffsetWidth();
		this.innerHd.firstChild.firstChild.style.width = tw;
		this.mainBody.dom.style.width = tw;
		var display = hidden ? 'none' : '';

		var hd = this.getHeaderCell(col);
		hd.style.display = display;

		var ns = this.getRows(), row, cell;
		for (var i = 0, len = ns.length; i < len; i++) {
			row = ns[i];
			row.style.width = tw;
			if (row.firstChild) {
				row.firstChild.style.width = tw;
				if (col < row.firstChild.rows[0].childNodes.length) {
					if (!Ext.fly(row.firstChild.rows[0].childNodes[col])
							.hasClass(this.skip_width_update_class)) {
						row.firstChild.rows[0].childNodes[col].style.display = display;
					}
				}
			}
		}

		this.onColumnHiddenUpdated(col, hidden, tw);
		delete this.lastViewWidth; // force recalc
		this.layout();
	},

	// private - overriden to skip hidden rows processing.
	processRows : function(startRow, skipStripe) {
		var processed_cnt = 0;

		if (this.ds.getCount() < 1) {
			return;
		}
		skipStripe = !this.grid.stripeRows; // skipStripe ||
		// !this.grid.stripeRows;
		startRow = startRow || 0;
		var rows = this.getRows();
		var processed_cnt = 0;

		Ext.each(rows, function(row, idx) {
					row.rowIndex = idx;
					row.className = row.className.replace(this.rowClsRe, ' ');
					if (row.style.display != 'none') {
						if (!skipStripe && ((processed_cnt + 1) % 2 === 0)) {
							row.className += ' x-grid3-row-alt';
						}
						processed_cnt++;
					}
				}, this);

		Ext.fly(rows[0]).addClass(this.firstRowCls);
		Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
	},

	ensureVisible : function(row, col, hscroll) {
		var ancestors, record = this.ds.getAt(row);

		if (!this.ds.isVisibleNode(record)) {
			ancestors = this.ds.getNodeAncestors(record);
			while (ancestors.length > 0) {
				record = ancestors.shift();
				if (!this.ds.isExpandedNode(record)) {
					this.ds.expandNode(record);
				}
			}
		}

		return Ext.ux.maximgb.tg.GridView.superclass.ensureVisible.call(this,
				row, col, hscroll);
	},

	// Private
	expandRow : function(record, skip_process) {
		var ds = this.ds, i, len, row, pmel, children, index, child_index;

		if (typeof record == 'number') {
			index = record;
			record = ds.getAt(index);
		} else {
			index = ds.indexOf(record);
		}

		skip_process = skip_process || false;

		row = this.getRow(index);
		pmel = Ext.fly(row).child('.ux-maximgb-tg-elbow-active');
		if (pmel) {
			if (ds.hasNextSiblingNode(record)) {
				pmel.removeClass(this.collapsed_icon_class);
				pmel.removeClass(this.last_collapsed_icon_class);
				pmel.addClass(this.expanded_icon_class);
			} else {
				pmel.removeClass(this.collapsed_icon_class);
				pmel.removeClass(this.last_collapsed_icon_class);
				pmel.addClass(this.last_expanded_icon_class);
			}
		}
		if (ds.isVisibleNode(record)) {
			children = ds.getNodeChildren(record);
			for (i = 0, len = children.length; i < len; i++) {
				child_index = ds.indexOf(children[i]);
				row = this.getRow(child_index);
				row.style.display = 'block';
				if (ds.isExpandedNode(children[i])) {
					this.expandRow(child_index, true);
				}
			}
		}
		if (!skip_process) {
			this.processRows(0);
		}
		// this.updateAllColumnWidths();
	},

	collapseRow : function(record, skip_process) {
		var ds = this.ds, i, len, children, row, index, child_index;

		if (typeof record == 'number') {
			index = record;
			record = ds.getAt(index);
		} else {
			index = ds.indexOf(record);
		}

		skip_process = skip_process || false;

		row = this.getRow(index);
		pmel = Ext.fly(row).child('.ux-maximgb-tg-elbow-active');
		if (pmel) {
			if (ds.hasNextSiblingNode(record)) {
				pmel.removeClass(this.expanded_icon_class);
				pmel.removeClass(this.last_expanded_icon_class);
				pmel.addClass(this.collapsed_icon_class);
			} else {
				pmel.removeClass(this.expanded_icon_class);
				pmel.removeClass(this.last_expanded_icon_class);
				pmel.addClass(this.last_collapsed_icon_class);
			}
		}
		children = ds.getNodeChildren(record);
		for (i = 0, len = children.length; i < len; i++) {
			child_index = ds.indexOf(children[i]);
			row = this.getRow(child_index);
			if (row.style.display != 'none') {
				row.style.display = 'none';
				this.collapseRow(child_index, true);
			}
		}
		if (!skip_process) {
			this.processRows(0);
		}
		// this.updateAllColumnWidths();
	},

	/**
	 * @access private
	 */
	initData : function(ds, cm) {
		Ext.ux.maximgb.tg.GridView.superclass.initData.call(this, ds, cm);
		if (this.ds) {
			this.ds.un('expandnode', this.onStoreExpandNode, this);
			this.ds.un('collapsenode', this.onStoreCollapseNode, this);
		}
		if (ds) {
			ds.on('expandnode', this.onStoreExpandNode, this);
			ds.on('collapsenode', this.onStoreCollapseNode, this);
		}
	},

	onLoad : function(store, records, options) {
		var ridx;

		if (options
				&& options.params
				&& (options.params[store.paramNames.active_node] === null || store
						.indexOfId(options.params[store.paramNames.active_node]) == -1)) {
			Ext.ux.maximgb.tg.GridView.superclass.onLoad.call(this, store,
					records, options);
		}
	},

	onAdd : function(ds, records, index) {
		Ext.ux.maximgb.tg.GridView.superclass.onAdd.call(this, ds, records,
				index);
		if (this.mainWrap) {
			// this.updateAllColumnWidths();
			this.processRows(0);
		}
	},

	onRemove : function(ds, record, index, isUpdate) {
		Ext.ux.maximgb.tg.GridView.superclass.onRemove.call(this, ds, record,
				index, isUpdate);
		if (isUpdate !== true) {
			if (this.mainWrap) {
				// this.updateAllColumnWidths();
				this.processRows(0);
			}
		}
	},

	onUpdate : function(ds, record) {
		Ext.ux.maximgb.tg.GridView.superclass.onUpdate.call(this, ds, record);
		if (this.mainWrap) {
			// this.updateAllColumnWidths();
			this.processRows(0);
		}
	},

	onStoreExpandNode : function(store, rc) {
		this.expandRow(rc);
	},

	onStoreCollapseNode : function(store, rc) {
		this.collapseRow(rc);
	}
});

Ext.ux.maximgb.tg.GridPanel = Ext.extend(Ext.grid.GridPanel, {
			/**
			 * @cfg {String|Integer} master_column_id Master column id. Master
			 *      column cells are nested. Master column cell values are used
			 *      to build breadcrumbs.
			 */
			master_column_id : 0,

			/**
			 * @cfg {Stirng} TreeGrid panel custom class.
			 */
			tg_cls : 'ux-maximgb-tg-panel',

			// Private
			initComponent : function() {
				this.initComponentPreOverride();
				Ext.ux.maximgb.tg.GridPanel.superclass.initComponent.call(this);
				this.getSelectionModel().on('selectionchange',
						this.onTreeGridSelectionChange, this);
				this.initComponentPostOverride();
			},

			initComponentPreOverride : Ext.emptyFn,

			initComponentPostOverride : Ext.emptyFn,

			// Private
			onRender : function(ct, position) {
				Ext.ux.maximgb.tg.GridPanel.superclass.onRender.call(this, ct,
						position);
				this.el.addClass(this.tg_cls);
			},

			/**
			 * Returns view instance.
			 * 
			 * @access private
			 * @return {GridView}
			 */
			getView : function() {
				if (!this.view) {
					this.view = new Ext.ux.maximgb.tg.GridView(this.viewConfig);
				}
				return this.view;
			},

			/**
			 * @access private
			 */
			onClick : function(e) {
				var target = e.getTarget(), view = this.getView(), row = view
						.findRowIndex(target), store = this.getStore(), sm = this
						.getSelectionModel(), record, record_id, do_default = true;

				// Row click
				if (row !== false) {
					if (Ext.fly(target).hasClass('ux-maximgb-tg-elbow-active')) {
						record = store.getAt(row);
						if (store.isExpandedNode(record)) {
							store.collapseNode(record);
						} else {
							store.expandNode(record);
						}
						do_default = false;
					}
				}

				if (do_default) {
					Ext.ux.maximgb.tg.GridPanel.superclass.onClick
							.call(this, e);
				}
			},

			/**
			 * @access private
			 */
			onMouseDown : function(e) {
				var target = e.getTarget();

				if (!Ext.fly(target).hasClass('ux-maximgb-tg-elbow-active')) {
					Ext.ux.maximgb.tg.GridPanel.superclass.onMouseDown.call(
							this, e);
				}
			},

			/**
			 * @access private
			 */
			onTreeGridSelectionChange : function(sm, selection) {
				var record, ancestors, store = this.getStore();
				// Row selection model
				if (sm.getSelected) {
					record = sm.getSelected();
					store.setActiveNode(record);
				}
				// Cell selection model
				else if (sm.getSelectedCell && selection) {
					record = selection.record;
					store.setActiveNode(record);
				}

				// Ensuring that selected node is visible.
				if (record) {
					if (!store.isVisibleNode(record)) {
						ancestors = store.getNodeAncestors(record);
						while (ancestors.length > 0) {
							store.expandNode(ancestors.pop());
						}
					}
				}
			}
		});

Ext.ux.maximgb.tg.EditorGridPanel = Ext.extend(Ext.grid.EditorGridPanel, {
			/**
			 * @cfg {String|Integer} master_column_id Master column id. Master
			 *      column cells are nested. Master column cell values are used
			 *      to build breadcrumbs.
			 */
			master_column_id : 0,

			// Private
			initComponent : function() {
				this.initComponentPreOverride();

				Ext.ux.maximgb.tg.EditorGridPanel.superclass.initComponent
						.call(this);

				this.getSelectionModel().on('selectionchange',
						this.onTreeGridSelectionChange, this);

				this.initComponentPostOverride();
			},

			initComponentPreOverride : Ext.emptyFn,

			initComponentPostOverride : Ext.emptyFn,

			// Private
			onRender : function(ct, position) {
				Ext.ux.maximgb.tg.EditorGridPanel.superclass.onRender.call(
						this, ct, position);
				this.el.addClass('ux-maximgb-tg-panel');
			},

			/**
			 * Returns view instance.
			 * 
			 * @access private
			 * @return {GridView}
			 */
			getView : function() {
				if (!this.view) {
					this.view = new Ext.ux.maximgb.tg.GridView(this.viewConfig);
				}
				return this.view;
			},

			/**
			 * @access private
			 */
			onClick : function(e) {
				var target = e.getTarget(), view = this.getView(), row = view
						.findRowIndex(target), store = this.getStore(), sm = this
						.getSelectionModel(), record, record_id, do_default = true;

				// Row click
				if (row !== false) {
					if (Ext.fly(target).hasClass('ux-maximgb-tg-elbow-active')) {
						record = store.getAt(row);
						if (store.isExpandedNode(record)) {
							store.collapseNode(record);
						} else {
							store.expandNode(record);
						}
						do_default = false;
					}
				}

				if (do_default) {
					Ext.ux.maximgb.tg.EditorGridPanel.superclass.onClick.call(
							this, e);
				}
			},

			/**
			 * @access private
			 */
			onMouseDown : function(e) {
				var target = e.getTarget();

				if (!Ext.fly(target).hasClass('ux-maximgb-tg-elbow-active')) {
					Ext.ux.maximgb.tg.EditorGridPanel.superclass.onMouseDown
							.call(this, e);
				}
			},

			/**
			 * @access private
			 */
			onTreeGridSelectionChange : function(sm, selection) {
				var record, ancestors, store = this.getStore();
				// Row selection model
				if (sm.getSelected) {
					record = sm.getSelected();
					store.setActiveNode(record);
				}
				// Cell selection model
				else if (sm.getSelectedCell && selection) {
					record = selection.record;
					store.setActiveNode(record);
				}

				// Ensuring that selected node is visible.
				if (record) {
					if (!store.isVisibleNode(record)) {
						ancestors = store.getNodeAncestors(record);
						while (ancestors.length > 0) {
							store.expandNode(ancestors.pop());
						}
					}
				}
			}
		});

/**
 * Paging toolbar for work this AbstractTreeStore.
 */
Ext.ux.maximgb.tg.PagingToolbar = Ext.extend(Ext.PagingToolbar, {
			onRender : function(ct, position) {
				Ext.ux.maximgb.tg.PagingToolbar.superclass.onRender.call(this,
						ct, position);
				this.updateUI();
			},

			getPageData : function() {
				var total = 0, cursor = 0;
				if (this.store) {
					cursor = this.store.getActiveNodePageOffset();
					total = this.store.getActiveNodeTotalCount();
				}
				return {
					total : total,
					activePage : Math.ceil((cursor + this.pageSize)
							/ this.pageSize),
					pages : total < this.pageSize ? 1 : Math.ceil(total
							/ this.pageSize)
				};
			},

			updateInfo : function() {
				var count = 0, cursor = 0, total = 0, msg;
				if (this.displayItem) {
					if (this.store) {
						cursor = this.store.getActiveNodePageOffset();
						count = this.store.getActiveNodeCount();
						total = this.store.getActiveNodeTotalCount();
					}
					msg = count == 0 ? this.emptyMsg : String.format(
							this.displayMsg, cursor + 1, cursor + count, total);
					this.displayItem.setText(msg);
				}
			},

			updateUI : function() {
				var d = this.getPageData(), ap = d.activePage, ps = d.pages;

				this.afterTextItem.setText(String.format(this.afterPageText,
						d.pages));
				this.inputItem.setValue(ap);

				this.first.setDisabled(ap == 1);
				this.prev.setDisabled(ap == 1);
				this.next.setDisabled(ap == ps);
				this.last.setDisabled(ap == ps);
				this.refresh.enable();
				this.updateInfo();
			},

			bindStore : function(store, initial) {
				if (!initial && this.store) {
					this.store.un('activenodechange',
							this.onStoreActiveNodeChange, this);
				}
				if (store) {
					store.on('activenodechange', this.onStoreActiveNodeChange,
							this);
				}
				Ext.ux.maximgb.tg.PagingToolbar.superclass.bindStore.call(this,
						store, initial);
			},

			beforeLoad : function(store, options) {
				var paramNames = this.getParams();

				Ext.ux.maximgb.tg.PagingToolbar.superclass.beforeLoad.call(
						this, store, options);

				if (options && options.params) {
					if (options.params[paramNames.start] === undefined) {
						options.params[paramNames.start] = 0;
					}
					if (options.params[paramNames.limit] === undefined) {
						options.params[paramNames.limit] = this.pageSize;
					}
				}
			},

			/**
			 * Move to the first page, has the same effect as clicking the
			 * 'first' button.
			 */
			moveFirst : function() {
				this.doLoad(0);
			},

			/**
			 * Move to the previous page, has the same effect as clicking the
			 * 'previous' button.
			 */
			movePrevious : function() {
				var store = this.store, cursor = store ? store
						.getActiveNodePageOffset() : 0;

				this.doLoad(Math.max(0, cursor - this.pageSize));
			},

			/**
			 * Move to the next page, has the same effect as clicking the 'next'
			 * button.
			 */
			moveNext : function() {
				var store = this.store, cursor = store ? store
						.getActiveNodePageOffset() : 0;

				this.doLoad(cursor + this.pageSize);
			},

			/**
			 * Move to the last page, has the same effect as clicking the 'last'
			 * button.
			 */
			moveLast : function() {
				var store = this.store, cursor = store ? store
						.getActiveNodePageOffset() : 0, total = store ? store
						.getActiveNodeTotalCount() : 0, extra = total
						% this.pageSize;

				this.doLoad(extra ? (total - extra) : total - this.pageSize);
			},

			onStoreActiveNodeChange : function(store, old_rec, new_rec) {
				if (this.rendered) {
					this.updateUI();
				}
			}
		});

Ext.reg('Ext.ux.maximgb.tg.GridPanel', Ext.ux.maximgb.tg.GridPanel);
Ext.reg('Ext.ux.maximgb.tg.EditorGridPanel', Ext.ux.maximgb.tg.EditorGridPanel);
Ext.reg('Ext.ux.maximgb.tg.PagingToolbar', Ext.ux.maximgb.tg.PagingToolbar);
